package jaux.tank.api.config;

import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.proxy.LazyInitializer;
import org.springframework.http.converter.json.AbstractJsonHttpMessageConverter;
import org.springframework.lang.Nullable;

import jaux.tank.utils.DateUtil;
import jaux.tank.utils.HttpKit;
import jodd.json.JsonParser;
import jodd.json.JsonSerializer;
import jodd.json.TypeJsonSerializer;
import jodd.json.impl.IterableJsonSerializer;
import jodd.typeconverter.TypeConverterManager;
import jodd.util.Wildcard;
import springfox.documentation.spring.web.json.Json;

public class JoddHttpMessageConverter  extends AbstractJsonHttpMessageConverter {
	
	

	public JoddHttpMessageConverter() {
		super();
	}

	private JsonSerializer genSerializer() {
		return JsonSerializer.create()
				.excludeNulls(true)
				.excludeTypes(LazyInitializer.class
						,PersistentCollection.class )
				.excludeTypes(InputStream.class)
			    .excludeTypes("javax.*")
				.deep(true)
				.withSerializer(Json.class, (TypeJsonSerializer<Json>) (jsonContext, value) -> {
					jsonContext.write(value.value());
					return true;
				})
				.withSerializer(Date.class, (TypeJsonSerializer<Date>) (jsonContext, value) -> {
					jsonContext.writeString(DateUtil.getTime(value));
					return true;
				});
	}

	@Override
	protected Object readInternal(Type resolvedType, Reader reader) throws Exception {
		final String string = IOUtils.toString(reader);
		if(StringUtils.isEmpty(string))
			return null;
		return new NewJsonParser()
				.strictTypes(false)
				.parse(string, (Class<?>)resolvedType);
	}

	
	@Override
	protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception {
		HttpServletRequest request = HttpKit.getRequest();
		Enumeration<String> includes = request.getHeaders("Response-Body-Include");
		Enumeration<String> excludes = request.getHeaders("Response-Body-Exclude");
		Enumeration<String> excludeNulls = request.getHeaders("Response-Body-Exclude-Nulls");
		JsonSerializer locSerializer =   genSerializer();
		List<String> includePaths = new ArrayList<>();
		while(includes.hasMoreElements()) {
			List<String> paths = Arrays.stream(includes.nextElement().split(",")).map(s->s.trim()).collect(Collectors.toList());
			includePaths.addAll(paths);
			locSerializer.include(paths.toArray(new String[paths.size()]));
		}
		while(excludes.hasMoreElements()) {
			locSerializer.exclude(Arrays.stream(excludes.nextElement().split(",")).map(s->s.trim()).toArray(String[]::new));
		}
		while(excludeNulls.hasMoreElements()) {
			locSerializer.excludeNulls(Arrays.stream(excludeNulls.nextElement().split(",")).anyMatch(s->Boolean.parseBoolean(s.trim())));
		}
		locSerializer.serialize(o, writer);
	}
	

}
class NewJsonParser extends JsonParser{
	@Override
	protected Object convertType(final Object value, final Class targetType) {
		Object result = super.convertType(value, targetType);
		if(result !=null) {
			return result;
		}
		// handle enums
		if ( targetType.isEnum()) {
			final Object[] enums = targetType.getEnumConstants();
			final String valStr = value.toString();
			for (final Object e : enums) {
				if (((Enum)e).name().equals(valStr)) {
					return  e;
				}
			}
			
		}else if (targetType == Date.class) {
			return DateUtil.parseTime(value.toString());
		}
		return result;
	}
	
}
